home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / sendmail.8.8.4.tar.gz / sendmail.8.8.4.tar / sendmail-8.8.4 / contrib / bitdomain.c next >
C/C++ Source or Header  |  1995-10-27  |  9KB  |  410 lines

  1. /*
  2.  * By John G. Myers, jgm+@cmu.edu
  3.  * Version 1.2
  4.  *
  5.  * Process a BITNET "internet.listing" file, producing output
  6.  * suitable for input to makemap.
  7.  *
  8.  * The input file can be obtained via anonymous FTP to bitnic.educom.edu.
  9.  * Change directory to "netinfo" and get the file internet.listing
  10.  * The file is updated monthly.
  11.  *
  12.  * Feed the output of this program to "makemap hash /etc/bitdomain.db"
  13.  * to create the table used by the "FEATURE(bitdomain)" config file macro.
  14.  * If your sendmail does not have the db library compiled in, you can instead
  15.  * use "makemap dbm /etc/bitdomain" and
  16.  * "FEATURE(bitdomain,`dbm -o /etc/bitdomain')"
  17.  *
  18.  * The bitdomain table should be rebuilt monthly.
  19.  */
  20.  
  21. #include <stdio.h>
  22. #include <errno.h>
  23. #include <sys/types.h>
  24. #include <netinet/in.h>
  25. #include <arpa/nameser.h>
  26. #include <resolv.h>
  27. #include <netdb.h>
  28. #include <ctype.h>
  29. #include <string.h>
  30.  
  31. /* don't use sizeof because sizeof(long) is different on 64-bit machines */
  32. #define SHORTSIZE    2    /* size of a short (really, must be 2) */
  33. #define LONGSIZE    4    /* size of a long (really, must be 4) */
  34.  
  35. typedef union
  36. {
  37.     HEADER    qb1;
  38.     char    qb2[PACKETSZ];
  39. } querybuf;
  40.  
  41. extern int h_errno;
  42. extern char *malloc();
  43. extern char *optarg;
  44. extern int optind;
  45.  
  46. char *lookup();
  47.  
  48. main(argc, argv)
  49. int argc;
  50. char **argv;
  51. {
  52.     int opt;
  53.  
  54.     while ((opt = getopt(argc, argv, "o:")) != EOF) {
  55.     switch (opt) {
  56.     case 'o':
  57.         if (!freopen(optarg, "w", stdout)) {
  58.         perror(optarg);
  59.         exit(1);
  60.         }
  61.         break;
  62.  
  63.     default:
  64.         fprintf(stderr, "usage: %s [-o outfile] [internet.listing]\n",
  65.             argv[0]);
  66.         exit(1);
  67.     }
  68.     }
  69.  
  70.     if (optind < argc) {
  71.     if (!freopen(argv[optind], "r", stdin)) {
  72.         perror(argv[optind]);
  73.         exit(1);
  74.     }
  75.     }
  76.     readfile(stdin);
  77.     finish();
  78.     exit(0);
  79. }
  80.  
  81. /*
  82.  * Parse and process an input file
  83.  */
  84. readfile(infile)
  85. FILE *infile;
  86. {
  87.     int skippingheader = 1;
  88.     char buf[1024], *node, *hostname, *p;
  89.  
  90.     while (fgets(buf, sizeof(buf), infile)) {
  91.     for (p = buf; *p && isspace(*p); p++);
  92.     if (!*p) {
  93.         skippingheader = 0;
  94.         continue;
  95.     }
  96.     if (skippingheader) continue;
  97.  
  98.     node = p;
  99.     for (; *p && !isspace(*p); p++) {
  100.         if (isupper(*p)) *p = tolower(*p);
  101.     }
  102.     if (!*p) {
  103.         fprintf(stderr, "%-8s: no domain name in input file\n", node);
  104.         continue;
  105.     }
  106.     *p++ = '\0';
  107.  
  108.     for (; *p && isspace(*p); p++) ;
  109.     if (!*p) {
  110.         fprintf(stderr, "%-8s no domain name in input file\n", node);
  111.         continue;
  112.     }
  113.  
  114.     hostname = p;
  115.     for (; *p && !isspace(*p); p++) {
  116.         if (isupper(*p)) *p = tolower(*p);
  117.     }
  118.     *p = '\0';
  119.  
  120.     /* Chop off any trailing .bitnet */
  121.     if (strlen(hostname) > 7 &&
  122.         !strcmp(hostname+strlen(hostname)-7, ".bitnet")) {
  123.         hostname[strlen(hostname)-7] = '\0';
  124.     }
  125.     entry(node, hostname, sizeof(buf)-(hostname - buf));
  126.     }
  127. }
  128.  
  129. /*
  130.  * Process a single entry in the input file.
  131.  * The entry tells us that "node" expands to "domain".
  132.  * "domain" can either be a domain name or a bitnet node name
  133.  * The buffer pointed to by "domain" may be overwritten--it
  134.  * is of size "domainlen".
  135.  */
  136. entry(node, domain, domainlen)
  137. char *node;
  138. char *domain;
  139. char *domainlen;
  140. {
  141.     char *otherdomain, *p, *err;
  142.  
  143.     /* See if we have any remembered information about this node */
  144.     otherdomain = lookup(node);
  145.  
  146.     if (otherdomain && strchr(otherdomain, '.')) {
  147.     /* We already have a domain for this node */
  148.     if (!strchr(domain, '.')) {
  149.         /*
  150.          * This entry is an Eric Thomas FOO.BITNET kludge.
  151.          * He doesn't want LISTSERV to do transitive closures, so we
  152.          * do them instead.  Give the the domain expansion for "node"
  153.          * (which is in "otherdomian") to FOO (which is in "domain")
  154.          * if "domain" doesn't have a domain expansion already.
  155.          */
  156.         p = lookup(domain);
  157.         if (!p || !strchr(p, '.')) remember(domain, otherdomain);
  158.     }
  159.     }
  160.     else {
  161.     if (!strchr(domain, '.') || valhost(domain, domainlen)) {
  162.         remember(node, domain);
  163.         if (otherdomain) {
  164.         /*
  165.          * We previously mapped the node "node" to the node
  166.          * "otherdomain".  If "otherdomain" doesn't already
  167.          * have a domain expansion, give it the expansion "domain".
  168.          */
  169.         p = lookup(otherdomain);
  170.         if (!p || !strchr(p, '.')) remember(otherdomain, domain);
  171.         }
  172.     }
  173.     else {
  174.         switch (h_errno) {
  175.         case HOST_NOT_FOUND:
  176.         err = "not registered in DNS";
  177.         break;
  178.  
  179.         case TRY_AGAIN:
  180.         err = "temporary DNS lookup failure";
  181.         break;
  182.  
  183.         case NO_RECOVERY:
  184.         err = "non-recoverable nameserver error";
  185.         break;
  186.  
  187.         case NO_DATA:
  188.         err = "registered in DNS, but not mailable";
  189.         break;
  190.         
  191.         default:
  192.         err = "unknown nameserver error";
  193.         break;
  194.         }
  195.  
  196.         fprintf(stderr, "%-8s %s %s\n", node, domain, err);
  197.     }
  198.     }
  199. }
  200.  
  201. /*
  202.  * Validate whether the mail domain "host" is registered in the DNS.
  203.  * If "host" is a CNAME, it is expanded in-place if the expansion fits
  204.  * into the buffer of size "hbsize".  Returns nonzero if it is, zero
  205.  * if it is not.  A BIND error code is left in h_errno.
  206.  */
  207. int
  208. valhost(host, hbsize)
  209.     char *host;
  210.     int hbsize;
  211. {
  212.     register u_char *eom, *ap;
  213.     register int n; 
  214.     HEADER *hp;
  215.     querybuf answer;
  216.     int ancount, qdcount;
  217.     int ret;
  218.     int type;
  219.     int qtype;
  220.     char nbuf[1024];
  221.  
  222.     if ((_res.options & RES_INIT) == 0 && res_init() == -1)
  223.         return (0);
  224.  
  225.     _res.options &= ~(RES_DNSRCH|RES_DEFNAMES);
  226.     _res.retrans = 30;
  227.     _res.retry = 10;
  228.  
  229.     qtype = T_ANY;
  230.  
  231.     for (;;) {
  232.         h_errno = NO_DATA;
  233.         ret = res_querydomain(host, "", C_IN, qtype,
  234.                       &answer, sizeof(answer));
  235.         if (ret <= 0)
  236.         {
  237.             if (errno == ECONNREFUSED || h_errno == TRY_AGAIN)
  238.             {
  239.                 /* the name server seems to be down */
  240.                 h_errno = TRY_AGAIN;
  241.                 return 0;
  242.             }
  243.  
  244.             if (h_errno != HOST_NOT_FOUND)
  245.             {
  246.                 /* might have another type of interest */
  247.                 if (qtype == T_ANY)
  248.                 {
  249.                     qtype = T_A;
  250.                     continue;
  251.                 }
  252.                 else if (qtype == T_A)
  253.                 {
  254.                     qtype = T_MX;
  255.                     continue;
  256.                 }
  257.             }
  258.  
  259.             /* otherwise, no record */
  260.             return 0;
  261.         }
  262.  
  263.         /*
  264.         **  This might be a bogus match.  Search for A, MX, or
  265.         **  CNAME records.
  266.         */
  267.  
  268.         hp = (HEADER *) &answer;
  269.         ap = (u_char *) &answer + sizeof(HEADER);
  270.         eom = (u_char *) &answer + ret;
  271.  
  272.         /* skip question part of response -- we know what we asked */
  273.         for (qdcount = ntohs(hp->qdcount); qdcount--; ap += ret + QFIXEDSZ)
  274.         {
  275.             if ((ret = dn_skipname(ap, eom)) < 0)
  276.             {
  277.                 return 0;        /* ???XXX??? */
  278.             }
  279.         }
  280.  
  281.         for (ancount = ntohs(hp->ancount); --ancount >= 0 && ap < eom; ap += n)
  282.         {
  283.             n = dn_expand((u_char *) &answer, eom, ap,
  284.                       (u_char *) nbuf, sizeof nbuf);
  285.             if (n < 0)
  286.                 break;
  287.             ap += n;
  288.             GETSHORT(type, ap);
  289.             ap += SHORTSIZE + LONGSIZE;
  290.             GETSHORT(n, ap);
  291.             switch (type)
  292.             {
  293.               case T_MX:
  294.               case T_A:
  295.                 return 1;
  296.  
  297.               case T_CNAME:
  298.                 /* value points at name */
  299.                 if ((ret = dn_expand((u_char *)&answer,
  300.                     eom, ap, (u_char *)nbuf, sizeof(nbuf))) < 0)
  301.                     break;
  302.                 if (strlen(nbuf) < hbsize) {
  303.                     (void)strcpy(host, nbuf);
  304.                 }
  305.                 return 1;
  306.  
  307.               default:
  308.                 /* not a record of interest */
  309.                 continue;
  310.             }
  311.         }
  312.  
  313.         /*
  314.         **  If this was a T_ANY query, we may have the info but
  315.         **  need an explicit query.  Try T_A, then T_MX.
  316.         */
  317.  
  318.         if (qtype == T_ANY)
  319.             qtype = T_A;
  320.         else if (qtype == T_A)
  321.             qtype = T_MX;
  322.         else
  323.             return 0;
  324.     }
  325. }
  326.  
  327. struct entry {
  328.     struct entry *next;
  329.     char *node;
  330.     char *domain;
  331. };
  332. struct entry *firstentry;
  333.  
  334. /*
  335.  * Find any remembered information about "node"
  336.  */
  337. char *lookup(node)
  338. char *node;
  339. {
  340.     struct entry *p;
  341.  
  342.     for (p = firstentry; p; p = p->next) {
  343.     if (!strcmp(node, p->node)) {
  344.         return p->domain;
  345.     }
  346.     }
  347.     return 0;
  348. }
  349.  
  350. /*
  351.  * Mark the node "node" as equivalent to "domain".  "domain" can either
  352.  * be a bitnet node or a domain name--if it is the latter, the mapping
  353.  * will be written to stdout.
  354.  */
  355. remember(node, domain)
  356. char *node;
  357. char *domain;
  358. {
  359.     struct entry *p;
  360.  
  361.     if (strchr(domain, '.')) {
  362.     fprintf(stdout, "%-8s %s\n", node, domain);
  363.     }
  364.  
  365.     for (p = firstentry; p; p = p->next) {
  366.     if (!strcmp(node, p->node)) {
  367.         p->domain = malloc(strlen(domain)+1);
  368.         if (!p->domain) {
  369.         goto outofmemory;
  370.         }
  371.         strcpy(p->domain, domain);
  372.         return;
  373.     }
  374.     }
  375.  
  376.     p = (struct entry *)malloc(sizeof(struct entry));
  377.     if (!p) goto outofmemory;
  378.  
  379.     p->next = firstentry;
  380.     firstentry = p;
  381.     p->node = malloc(strlen(node)+1);
  382.     p->domain = malloc(strlen(domain)+1);
  383.     if (!p->node || !p->domain) goto outofmemory;
  384.     strcpy(p->node, node);
  385.     strcpy(p->domain, domain);
  386.     return;
  387.  
  388.   outofmemory:
  389.     fprintf(stderr, "Out of memory\n");
  390.     exit(1);
  391. }
  392.  
  393. /*
  394.  * Walk through the database, looking for any cases where we know
  395.  * node FOO is equivalent to node BAR and node BAR has a domain name.
  396.  * For those cases, give FOO the same domain name as BAR.
  397.  */
  398. finish()
  399. {
  400.     struct entry *p;
  401.     char *domain;
  402.  
  403.     for (p = firstentry; p; p = p->next) {
  404.     if (!strchr(p->domain, '.') && (domain = lookup(p->domain))) {
  405.         remember(p->node, domain);
  406.     }
  407.     }
  408. }
  409.         
  410.